
<template>
  <Teleport to="body">
    <div :id="id" class="dialog" :class="klass" v-bind="$attrs" ref="dialog">
      <div class="swal2-popup swal2-show form form-large" :class="{ 'form-vertical': form === 'vertical' }" :style="popupStyle">
        <div class="swal2-icon swal2-icon-show" v-if="icon && type === 'alert'">
          <div class="swal2-icon-content">
            <img src="/assets/icon.png" />
          </div>
        </div>
        <h2 class="dialog-title swal2-title" ref="title">
          <slot name="header"></slot>
        </h2>
        <div class="dialog-body swal2-html-container" ref="content" :style="bodyStyle">
          <slot name="body"></slot>
        </div>
        <div class="dialog-footer swal2-actions" ref="actions">
          <slot name="footer"></slot>
        </div>
      </div>
    </div>
  </Teleport>
</template>

<script setup lang="ts">

import { PropType, computed, nextTick, ref } from 'vue'

export type DialogType = 'alert' | 'window'
export type DialogForm = 'horizontal' | 'vertical'

const visible = ref(false)
const dialog = ref<HTMLElement|null>(null)
const title = ref<HTMLElement|null>(null)
const content = ref<HTMLElement|null>(null)
const actions = ref<HTMLElement|null>(null)

const emit = defineEmits(['save'])

const props = defineProps({
  id: {
    type: String,
    required: true
  },
  dismissible: {
    type: Boolean,
    default: true
  },
  width: {
    type: String,
    default: undefined
  },
  height: {
    type: String,
    default: undefined
  },
  type: {
    type: String as PropType<DialogType>,
    default: 'alert'
  },
  icon: {
    type: Boolean,
    default: false
  },
  form: {
    type: String as PropType<DialogForm>,
    default: 'vertical'
  },
  enterSaves: {
    type: Boolean,
    default: true
  }
})

const klass = computed(() => {
  return {
    'swal2-center': visible.value,
    'swal2-container': visible.value,
    'swal2-backdrop-show': visible.value,
    visible: visible.value,
    [props.type]: visible.value
  }
})

const popupStyle = computed(() => {
  return {
    ...(props.width ? {
      width: `${props.width} !important`,
      maxWidth: `${props.width} !important`
    } : {}),
  }
})

const bodyStyle = computed(() => {
  return {
    ...(props.height ? {
      height: `${props.height} !important`,
    } : {}),
  }
})

const show = async () => {

  // backwards compatibility with old dialogs
  const buttons = actions.value.querySelector('.buttons')
  if (buttons) {
    const children = Array.from(buttons.childNodes)
    for (let i = children.length - 1; i >= 0; i--) {

      // style it
      const button = children[i] as HTMLElement
      button.classList.add('swal2-styled')
      // if (button.classList.contains('default') || button.classList.contains('alert-confirm')) {
      //   button.classList.add('swal2-confirm')
      //   button.classList.add('primary')
      // } else {
      //   button.classList.add('swal2-cancel')
      //   button.classList.add('tertiary')
      // }

      // now move it
      actions.value.insertBefore(button, buttons)
    }

    // remove the container
    buttons.remove()
  }

  // now we can show it
  visible.value = true
  document.addEventListener('keydown', onKeyDown)

  // wait render
  await nextTick()

  // focus 1st input
  if (content.value) {
    const input = content.value.querySelector('textarea') ?? content.value.querySelector('input') 
    if (input) {
      input.focus()
      input.select()
      if ('scrollTo' in input) {
        input.scrollTo(0, 0)
      }
    }
  }

}

const close = () => {
  document.removeEventListener('keydown', onKeyDown)
  visible.value = false
}

const onKeyDown = (e: KeyboardEvent) => {
  if (e.key === 'Escape') {
    e.preventDefault()
    if (props.dismissible) {
      close()
    }
  } else if (e.key === 'Enter' && props.enterSaves) {
    const activeElement = document.activeElement
    if (activeElement && activeElement.tagName === 'TEXTAREA' && !activeElement.classList.contains('text-textarea')) {
      return
    }
    e.preventDefault()
    emit('save')
  }
}

defineExpose({ show, close })

</script>


<style scoped>

.dialog {
  display: none;
  &.visible {
    display: grid;
  }
}

</style>